﻿/*
 * This is a simple window manager that can be used to simplify
 * the management of WebViews.
*/

using AwesomiumDotNet;
using SlimDX;
using SlimDX.Direct3D9;
using SlimDX.RawInput;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace SlimDXSample
{
    public class UIManager
    {
        public SlimDX.Direct3D9.Device Device { get; private set; }

        public WebCore WebCore { get; private set; }
        public Form Form { get; private set; }
        public Sprite Sprite { get; private set; }

        List<UIView> views;

        public UIManager(SlimDX.Direct3D9.Device device, Form form)
        {
            Device = device;
            Form = form;

            views = new List<UIView>();

            // Initialize WebCore
            WebCoreConfig config = new WebCoreConfig();
            config.SetEnablePlugins(true);
            config.SetLogLevel(LogLevel.None);
            WebCore = new WebCore(config);
            WebCore.BaseDirectory = "html\\";

            Sprite = new Sprite(Device);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool isDisposing)
        {
            if (isDisposing)
            {
                foreach (UIView view in views)
                    view.Dispose();

                WebCore.Dispose();

                if (Sprite != null)
                {
                    Sprite.Dispose();
                    Sprite = null;
                }
            }
        }

        public void Add(UIView form)
        {
            views.Add(form);
        }

        public void AddToBottom(UIView form)
        {
            views.Insert(0, form);
        }

        public void Remove(UIView form)
        {
            views.Remove(form);
        }

        public void MouseEvent(Input input)
        {
            if (input.MousePressed == MouseButtonFlags.None
                && input.MouseDelta == Point.Empty
                && input.MouseWheelDelta == 0)
                return;

            Rectangle wndRect = new Rectangle();

            // Find the view that was clicked on
            foreach (UIView view in views)
            {
                wndRect.X = (int)(view.x - (int)view.cx);
                wndRect.Y = (int)(view.y - (int)view.cy);
                wndRect.Width = view.Width;
                wndRect.Height = view.Height;
                
                if (wndRect.Contains(input.MousePoint))
                {
                    view.MouseMove(input.MousePoint.X - wndRect.X, input.MousePoint.Y - wndRect.Y);

                    if ((input.MousePressed & MouseButtonFlags.LeftDown) == MouseButtonFlags.LeftDown)
                        view.MouseDown(MouseButton.Left);

                    if ((input.MousePressed & MouseButtonFlags.RightDown) == MouseButtonFlags.RightDown)
                        view.MouseDown(MouseButton.Right);

                    if ((input.MousePressed & MouseButtonFlags.LeftUp) == MouseButtonFlags.LeftUp)
                        view.MouseUp(MouseButton.Left);

                    if ((input.MousePressed & MouseButtonFlags.RightUp) == MouseButtonFlags.RightUp)
                        view.MouseUp(MouseButton.Right);

                    if (input.MouseWheelDelta != 0)
                    {
                        int delta = (input.MouseWheelDelta > 32768)
                            ? -(65536 - input.MouseWheelDelta) : input.MouseWheelDelta;
                        
                        view.WebView.InjectMouseWheel(delta);
                    }

                    break;
                }
            }
        }

        // Returns true if all keys were handled
        public bool KeyboardEvent(Input input)
        {
            if (input.KeysDown.Count == 0 && input.KeysPressed.Count == 0)
                return false;

            // Find a view that accepts input
            foreach (UIView view in views)
            {
                if (view.takesKeystrokes == true)
                {
                    view.KeyBoardEvent(input);
                    return true;
                }
            }
            return false;
        }

        public void OnResourceLoad()
        {
            Sprite.OnResetDevice();

            foreach (UIView view in views)
                view.CreateTexture();
        }

        public void OnResourceUnload()
        {
            Sprite.OnLostDevice();

            foreach (UIView view in views)
                view.ClearTexture();
        }

        public void DrawAll()
        {
            foreach (UIView view in views)
                view.Draw();
        }
    };

    [FlagsAttribute]
    public enum UIFlags
    {
        None = 0,
        CenterVertical = 1,
        CenterHorizontal = 2,
        Center = CenterVertical | CenterHorizontal,
        FitWidth = 4,
        FitHeight = 8,
        AlignLeft = 0,
        AlignRight = 16,
        AlignTop = 0,
        AlignBottom = 32,
        AlignBottomRight = AlignBottom | AlignRight
    };

    public class UIView : IDisposable
    {
        public UIManager UIManager { get; private set; }

        public WebView WebView { get; private set; }

        public int Width { get; private set; }
        public int Height { get; private set; }

        public bool IsLoading
        {
            get { return isLoading; }
            set { isLoading = value; }
        }

        public UIFlags UIFlags { get; private set; }

        public bool IsTransparent
        {
            get
            {
                return isTransparent;
            }
            set
            {
                isTransparent = value;
                WebView.SetTransparent(value);
                if (value)
                    WebView.ExecuteJavaScript("document.body.style.background = 'transparent';");
                CreateTexture();
            }
        }

        public float x = 0, y = 0;
        public float cx = 0, cy = 0;

        public bool pageLoaded = false;
        public bool takesKeystrokes = true;
        bool isLoading = false;
        bool isTransparent = true;
        bool redrawTexture = false;

        Texture tex = null;

        public UIView(UIManager ui, int width, int height, UIFlags flags)
        {
            this.UIManager = ui;
            this.Width = width;
            this.Height = height;
            this.UIFlags = flags;

            // Initialize Awesomium
            WebView = ui.WebCore.CreateWebView(width, height);
            WebView.FinishLoading += OnFinishLoading;
            WebView.ChangeCursor += OnChangeCursor;
            WebView.SetTransparent(isTransparent);
            WebView.Focus();

            ui.Add(this);
        }

        public UIView(UIManager ui, int width, int height)
            : this(ui, width, height, UIFlags.None)
        {
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool isDisposing)
        {
            if (isDisposing)
            {
                if (isLoading)
                {
                    ExecuteJavaScript("window.stop();");
                    System.Threading.Thread.Sleep(100);
                }

                WebView.Destroy();
                if (tex != null)
                {
                    tex.Dispose();
                    tex = null;
                }
            }
        }

        public void SetBottom()
        {
            UIManager.Remove(this);
            UIManager.AddToBottom(this);
        }

        void OnFinishLoading(object sender, FinishLoadingEventArgs e)
        {
            pageLoaded = true;
            isLoading = false;
            if (isTransparent)
                WebView.ExecuteJavaScript("document.body.style.background = 'transparent';");
        }

        void OnChangeCursor(object sender, ChangeCursorEventArgs e)
        {
            UIManager.Form.Cursor = e.Cursor;
        }

        public void CreateObject(String objectName)
        {
            WebView.CreateObject(objectName);
        }

        public void SetCallbackName(String objectName, String callbackName)
        {
            WebView.SetObjectCallback(objectName, callbackName);
        }

        public void SetCallback(EventHandler<CallbackEventArgs> callback)
        {
            WebView.Callback += callback;
        }

        public void MouseMove(int x, int y)
        {
            WebView.InjectMouseMove(x, y);
        }

        public void MouseDown(MouseButton key)
        {
            WebView.InjectMouseDown(key);
        }

        public void MouseUp(MouseButton key)
        {
            WebView.InjectMouseUp(key);
        }

        public void KeyBoardEvent(Input input)
        {
            foreach (Keys key in input.KeysPressed)
            {
                WebKeyboardEvent keyEvent = new WebKeyboardEvent();

                bool shiftPressed = false;
                bool controlPressed = false;

                if (input.KeysDown.Contains(Keys.ShiftKey))
                {
                    shiftPressed = true;
                    keyEvent.Modifiers |= Modifiers.ShiftKey;
                }

                if (input.KeysDown.Contains(Keys.ControlKey))
                {
                    controlPressed = true;
                    keyEvent.Modifiers |= Modifiers.ControlKey;
                }

                bool ischar = true;
                keyEvent.Text = Input.KeyToChar(key, shiftPressed, out ischar);
                if (keyEvent.Text != '\0')
                {
                    keyEvent.VirtualKeyCode = keyEvent.Text;
                    if (ischar)
                    {
                        if (controlPressed)
                        {
                            switch ((char)keyEvent.Text)
                            {
                                case 'a':
                                    WebView.SelectAll();
                                    continue;
                                case 'x':
                                    WebView.Cut();
                                    continue;
                                case 'c':
                                    WebView.Copy();
                                    continue;
                                case 'v':
                                    WebView.Paste();
                                    continue;
                            }
                        }

                        keyEvent.EventType = KeyboardEventType.KeyDownType;
                        WebView.InjectKeyboardEvent(keyEvent);
                        keyEvent.EventType = KeyboardEventType.CharType;
                        WebView.InjectKeyboardEvent(keyEvent);
                    }
                    else
                    {
                        keyEvent.EventType = KeyboardEventType.KeyDownType;
                        WebView.InjectKeyboardEvent(keyEvent);
                    }
                }
            }
        }

        public void LoadUrl(string url)
        {
            isLoading = true;
            WebView.LoadUrl(url);
        }

        public void LoadHtml(string html)
        {
            isLoading = true;
            WebView.LoadHtml(html);
        }

        public void LoadFile(string fileName)
        {
            isLoading = true;
            WebView.LoadFile(fileName);
        }

        public void ExecuteJavaScript(string javaScript)
        {
            WebView.ExecuteJavaScript(javaScript);
        }

        public void Draw()
        {
            if (pageLoaded == false)
                return;

            if (WebView.IsDirty || redrawTexture)
            {
                Surface surface = tex.GetSurfaceLevel(0);
                DataRectangle rect = surface.LockRectangle(LockFlags.Discard);
                IntPtr surface_ptr = rect.Data.DataPointer;

                RenderBuffer buffer = WebView.Render();

                buffer.CopyTo(rect.Data.DataPointer, rect.Pitch, 4, false);

                surface.UnlockRectangle();
                redrawTexture = false;
            }

            Vector3 center, position;
            center = position = Vector3.Zero;

            if ((UIFlags & UIFlags.CenterHorizontal) == UIFlags.CenterHorizontal)
            {
                center.X = cx;
                position.X = x;
            }
            if ((UIFlags & UIFlags.CenterVertical) == UIFlags.CenterVertical)
            {
                center.Y = cy;
                position.Y = y;
            }

            if ((UIFlags & UIFlags.AlignRight) == UIFlags.AlignRight)
            {
                center.X = cx;
                position.X = x;
            }
            if ((UIFlags & UIFlags.AlignBottom) == UIFlags.AlignBottom)
            {
                center.Y = cy;
                position.Y = y;
            }

            if (isTransparent)
                UIManager.Sprite.Begin(SpriteFlags.AlphaBlend);
            else
                UIManager.Sprite.Begin(SpriteFlags.None);
            UIManager.Sprite.Draw(tex, center, position, Color.White);
            UIManager.Sprite.End();
        }

        public void CreateTexture()
        {
            if ((UIFlags & UIFlags.FitWidth) == UIFlags.FitWidth)
                Width = UIManager.Device.Viewport.Width;

            if ((UIFlags & UIFlags.FitHeight) == UIFlags.FitHeight)
                Height = UIManager.Device.Viewport.Height;

            if ((UIFlags & UIFlags.CenterHorizontal) == UIFlags.CenterHorizontal)
                x = UIManager.Device.Viewport.Width / 2;
            if ((UIFlags & UIFlags.CenterVertical) == UIFlags.CenterVertical)
                y = UIManager.Device.Viewport.Height / 2;

            if ((UIFlags & UIFlags.CenterHorizontal) == UIFlags.CenterHorizontal)
                cx = Width / 2;
            if ((UIFlags & UIFlags.CenterVertical) == UIFlags.CenterVertical)
                cy = Height / 2;

            if ((UIFlags & UIFlags.AlignRight) == UIFlags.AlignRight)
            {
                x = UIManager.Device.Viewport.Width / 2;
                cx = Width - x;
            }
            if ((UIFlags & UIFlags.AlignBottom) == UIFlags.AlignBottom)
            {
                y = UIManager.Device.Viewport.Height / 2;
                cy = Height - y;
            }

            ClearTexture();
            tex = new Texture(UIManager.Device, Width, Height, 1, Usage.Dynamic,
                isTransparent ? Format.A8R8G8B8 : Format.X8R8G8B8, Pool.Default);
            WebView.Resize(Width, Height);
            
            redrawTexture = true;
        }

        public void ClearTexture()
        {
            if (tex != null)
            {
                tex.Dispose();
                tex = null;
            }
        }
    };


    public class UIInfoForm : UIView
    {
        public UIInfoForm(UIManager ui, int width, int height, UIFlags flags)
            : base(ui, width, height, flags)
        {
        }

        public UIInfoForm(UIManager ui)
            : this(ui, 200, 100, UIFlags.None)
        {
            LoadFile("info.html");
            takesKeystrokes = false;
        }

        public void SetText(string text)
        {
            ExecuteJavaScript("setText('" + text + "');");
        }
    }
}
